# StringParamedSQL.java

    package org.codefilarete.stalactite.sql.statement;

    import java.sql.PreparedStatement;
    import java.util.Map;

    import org.codefilarete.stalactite.sql.statement.binder.PreparedStatementWriter;
    import org.codefilarete.stalactite.sql.statement.binder.PreparedStatementWriterIndex;
    import org.codefilarete.stalactite.sql.statement.ExpandableSQL.ExpandableParameter;
    import org.codefilarete.stalactite.sql.statement.SQLParameterParser.ParsedSQL;

    /**
     * Class that applies values to {@link PreparedStatement} according to SQL that contains named parameters.
     * Supports parameters expansion when passing {@link Iterable} as value of a parameter.
     *
     * @author Guillaume Mary
     */
    public class StringParamedSQL extends ExpandableStatement<String> {

    	private final ParsedSQL parsedSQL;
    	private ExpandableSQL expandableSQL;
    	private boolean expandableSQLExpired = false;

    	public StringParamedSQL(String originalSQL, Map<String, ? extends PreparedStatementWriter<?>> parameterBinders) {
    		this(new SQLParameterParser(originalSQL).parse(), parameterBinders);
    	}

    	public StringParamedSQL(ParsedSQL parsedSQL, Map<String, ? extends PreparedStatementWriter<?>> parameterBinders) {
    		super(null, parameterBinders);
    		this.parsedSQL = parsedSQL;
    	}

    	public StringParamedSQL(String originalSQL, PreparedStatementWriterIndex<String, ? extends PreparedStatementWriter<?>> parameterBinderProvider) {
    		this(new SQLParameterParser(originalSQL).parse(), parameterBinderProvider);
    	}

    	public StringParamedSQL(ParsedSQL parsedSQL, PreparedStatementWriterIndex<String, ? extends PreparedStatementWriter<?>> parameterBinderProvider) {
    		super(null, parameterBinderProvider);
    		this.parsedSQL = parsedSQL;
    	}

    	public ParsedSQL getParsedSQL() {
    		return parsedSQL;
    	}

    	@Override
    	public void setValues(Map<String, ?> values) {
    		super.setValues(values);
    		expandableSQLExpired = true;
    	}

    	@Override
    	public void setValue(String index, Object value) {
    		super.setValue(index, value);
    		expandableSQLExpired = true;
    	}

    	@Override
    	public String getSQLSource() {
    		return this.parsedSQL.toString();
    	}

    	@Override
    	public String getSQL() {
    		ensureExpandableSQL(values);
    		return expandableSQL.getPreparedSQL();
    	}

    	protected void ensureExpandableSQL(Map<String, Object> values) {
    		if (expandableSQL == null || expandableSQLExpired) {
    			expandableSQL = new ExpandableSQL(this.parsedSQL, getValuesSizes(values));
    			expandableSQLExpired = false;
    		}
    	}

    	@Override
    	protected int[] getIndexes(String parameter) {
    		ExpandableParameter expandableParameter = expandableSQL.getExpandableParameters().get(getParameterName(parameter));
    		return expandableParameter.getMarkIndexes();
    	}

    	protected Map<String, Integer> getValuesSizes(Map<String, Object> values) {
    		return ExpandableSQL.sizes(values);
    	}

    	@Override
    	protected String getParameterName(String parameter) {
    		return parameter;
    	}
    }